package cn.tedu.eb_film.service.impl;

import cn.tedu.eb_film.ex.ServiceException;
import cn.tedu.eb_film.mapper.UserMapper;
import cn.tedu.eb_film.pojo.dto.*;
import cn.tedu.eb_film.pojo.entity.User;
import cn.tedu.eb_film.pojo.vo.MovieTypeStandardVO;
import cn.tedu.eb_film.pojo.vo.UserJwtInfo;
import cn.tedu.eb_film.pojo.vo.UserListItemVO;
import cn.tedu.eb_film.pojo.vo.UserStandardVO;
import cn.tedu.eb_film.security.UserDetailss;
import cn.tedu.eb_film.service.UserService;
import cn.tedu.eb_film.utils.*;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 用户表(User)表服务实现类
 *
 * @author HおE
 * @since 2023-03-14 18:38:25
 */
@Slf4j
@Service("userService")
public class UserServiceImpl implements UserService {

    @Value("${eb-movie.jwt.secret-key}")
    private String secretKey;

    @Value("${eb-movie.jwt.duration-is-in-minutes}")
    private Long durationInMinute;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserMapper userMapper;

    // 方法要将验证码保存到Redis中
    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;

    @Autowired
    private AuthenticationManager authenticationManager;

    // 使用Redis要定义一个常量,作为手机验证码key的名称
    public static final String PHONE_CODE_KEY = "phone_code";

    //定义全局变量手机号
    private String phone;

    /**
     * 新增数据
     *
     * @param userAddNewDTO 实例对象
     * @return 实例对象
     */
    @Override
    public void insert(UserAddNewDTO userAddNewDTO) {
        log.debug("开始执行添加业务，参数为：{}", userAddNewDTO);

        String name = userAddNewDTO.getUsername();
        int rows = userMapper.countByName(name);
        log.debug("根据用户名【{}【统计数量，结果为：{}", name, rows);
        if (rows > 0) {
            String message = "添加用户失败，用户名已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_DATA_PRESENCE, message);
        }

        String email = userAddNewDTO.getEmail();
        rows = userMapper.countByEmail(email);
        log.debug("根据用户名【{}【统计数量，结果为：{}", email, rows);
        if (rows > 0) {
            String message = "添加用户失败，邮箱已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_DATA_PRESENCE, message);
        }

        String identityCard = userAddNewDTO.getIdentityCard();
        rows = userMapper.countByIdentityCard(identityCard);
        log.debug("根据用户名【{}【统计数量，结果为：{}", identityCard, rows);
        if (rows > 0) {
            String message = "添加用户失败，身份证号已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_DATA_PRESENCE, message);
        }

        String phone = userAddNewDTO.getPhone();
        rows = userMapper.countByPhone(phone);
        log.debug("根据电话号码【{}【统计数量，结果为：{}", phone, rows);
        if (rows > 0) {
            String message = "添加用户失败，电话号码已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_DATA_PRESENCE, message);
        }
        log.debug("获取验证码的电话号码{}，传入的电话号码{}", this.phone, phone);
        if ("".equals(this.phone) || this.phone == null) {
            String message = "添加用户失败，未获取验证码";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
        }
        if (!this.phone.equals(phone)) {
            log.debug("获取验证码的电话号码{}，传入的电话号码{}：{}", this.phone, phone);
            String message = "添加用户失败，电话号码与之前输入不一致！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
        }
        //获取用户输入的验证码
        Integer getCode = userAddNewDTO.getPhoneCode();
        //从redis中获取验证码
        Integer code = (Integer) redisTemplate.opsForValue().get(PHONE_CODE_KEY);
        if (!getCode.equals(code)) {
            String message = "验证码错误或验证码已失效！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
        }
        User user = new User();
        BeanUtils.copyProperties(userAddNewDTO, user);
        //当前创建时间当前时间
        LocalDateTime localDateTime = LocalDateTime.now();
        //将当前时间添加为创建时间
        user.setCreateTime(localDateTime);
        //将当前时间添加为更新时间
        user.setUpdateTime(localDateTime);
        //设置用户状态1为正常
        user.setState(1);

        log.debug("即将添加用户，参数为：{}", user);
        //从Admin对象中取出原密码,执行加密,再将密文存入user对象中
        String rawPassword = user.getPassword();
        log.debug("即将用户密码，参数为：{}", rawPassword);
        String encodedPassword =passwordEncoder.encode(rawPassword);
        user.setPassword(encodedPassword);
        rows = userMapper.insert(user);
        if (rows != 1) {
            String message = "添加用户失败，请稍后再试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_UNKNOWN, message);
        }
        log.debug("添加用户成功，参数为：{}", user);

    }

    /**
     * 手机验证码
     *
     * @param phone 手机号
     */
    @Override
    public void seedCode(String phone) {
        //将前端传入的手机号赋值给全局变量phone
        this.phone = phone;

        //生成六位数随机验证码
        Integer code = Captcha.getCode();
        log.debug("该手机号：{}的验证码为：{},", phone, code);
        //设置验证码的过期时间为五分钟
        redisTemplate.boundValueOps(PHONE_CODE_KEY).set(
                code,
                5,
                TimeUnit.MINUTES);
        try {
            //根据手机号发送验证码
            SmsUtils.sendSms(phone, code);
            log.debug("发送成功，该手机号：{}的验证码为：{},", phone, code);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 用户账号密码登录
     *
     * @param userLoginDto 登录信息
     * @return
     */
    @Override
    public UserJwtInfo accountLogin(UserLoginDto userLoginDto) {

        String username = userLoginDto.getUsername();
        log.debug("真正处理用户账号密码登录，用户名为：{}", username);
        int rows = userMapper.countByName(username);
        log.debug("根据用户名【{}【统计数量，结果为：{}", username, rows);
        Authentication authentication = new UsernamePasswordAuthenticationToken(
               userLoginDto.getUsername() ,userLoginDto.getPassword());

        System.out.println("authentication = " + authentication);

        final Authentication authenticate = authenticationManager.authenticate(authentication);

        System.out.println("authenticate = " + authenticate);

        if (Objects.isNull(authenticate)) {
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND,"认证失败用户信息不存在");
        }

        log.info(String.valueOf(authenticate));
        log.debug("认证通过！（如果未通过，过程中将抛出异常，你不会看到此条日志！）");

        UserDetailss userDetails = (UserDetailss) authenticate.getPrincipal();
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", userDetails.getId());
        claims.put("username", userDetails.getUsername());

        String authorities = JSON.toJSONString(userDetails.getAuthorities());
        String jwt = JwtUtils.createJwt(claims,secretKey,durationInMinute);
        claims.put("authorities",authorities);
        //新建对象userJwtInfo，封装用户id,用户昵称，jwt信息传入前端
        UserJwtInfo userJwtInfo = new UserJwtInfo();
        userJwtInfo.setUserId(userDetails.getId());
        userJwtInfo.setUsername(userDetails.getUsername());
        userJwtInfo.setJwt(jwt);
        return userJwtInfo;
    }


    /**
     * 用户手机号登录
     *
     * @param userAddNewDTO 登录信息
     */
    @Override
    public void phoneLogin(UserAddNewDTO userAddNewDTO) {
        String phone = userAddNewDTO.getPhone();
        log.debug("真正处理用户手机号登录，手机号为：{}", phone);
        int rows = userMapper.countByPhone(phone);
        log.debug("根据手机号【{}【统计数量，结果为：{}", phone, rows);
        if (rows == 0) {
            String message = "手机号错误或不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_DATA_PRESENCE, message);
        }
        //获取用户输入的验证码
        Integer getPhoneCode = userAddNewDTO.getPhoneCode();
        //从redis中获取验证码
        Integer phoneCode = (Integer) redisTemplate.opsForValue().get(PHONE_CODE_KEY);
        if (!getPhoneCode.equals(phoneCode)) {
            String message = "验证码错误或验证码已失效！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
        }
        log.debug("登录用户成功，手机号为：{}，验证码为：{}", phone, phoneCode);
    }

    /**
     * 判断手机号是否存在
     * @param phone 手机号
     */
    @Override
    public void countByPhone(String phone) {
        log.debug("判断手机号是否存在");
        int i = userMapper.countByPhone(phone);
        if (i == 0){
            String message = "手机号未注册，请注册后再登录";
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND,message);
        }
    }

    @Override
    public void see(UserSeeDTO userSeeDTO) {

        log.debug("======{},", userSeeDTO);
        UserStandardVO standardById = userMapper.getStandardById(userSeeDTO.getId());
        if (standardById == null) {
            log.error("无法找到用户信息，id={}", userSeeDTO.getId());
            return;
        }
        String typeStr = standardById.getMoviesWts();
        if (typeStr == null) {
            // 如果 moviesWts 字段为 null，则初始化为空字符串。
            typeStr = "";
        }
        if (Arrays.stream(typeStr.split(","))
                .noneMatch(s -> s.trim().equals(userSeeDTO.getMoviesWts()))) {
            StringBuilder stringBuilder = new StringBuilder(typeStr);
            if (stringBuilder.length() > 0) {
                stringBuilder.append(",");
            }
            stringBuilder.append(userSeeDTO.getMoviesWts());
            log.debug("----------{}", stringBuilder);
            User user = new User();
            user.setId(userSeeDTO.getId());
            user.setMoviesWts(stringBuilder.toString());
            log.debug("更改参数为{}", user);
            userMapper.update(user);
        }
    }


    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 是否成功
     */
    @Override
    public void deleteById(Long id) {
        log.debug("开始执行删除业务，ID为：{}", id);
        UserStandardVO userStandardVO = userMapper.getStandardById(id);
        log.debug("根据id查询用户,参数为：{}", userStandardVO);
        if (userStandardVO == null || userStandardVO.getState().equals(StateCode.STATE_DELETE.getValue())) {
            String message = "删除用户失败，用户不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, message);
        }

        log.debug("即将执行删除操作!删除参数为：{}", userStandardVO);
        int rows = userMapper.deleteById(id, StateCode.STATE_DELETE.getValue());
        if (rows != 1) {
            String message = "删除用户失败，请稍后再试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_UNKNOWN, message);
        }
        UserStandardVO userStandardVO1 = userMapper.getStandardById(id);
        log.debug("删除成功!删除参数为：{}", userStandardVO1);

    }

    /**
     * 修改数据
     *
     * @param userUpdateDTO 实例对象
     * @return 实例对象
     */
    @Override
    public void updateInfoById(Long id, UserUpdateDTO userUpdateDTO) {
        log.debug("开始执行修改业务,id为：{}，参数为：{}", id, userUpdateDTO);
        UserStandardVO userStandardVO = userMapper.getStandardById(id);
        log.debug("根据id查询用户,参数为：{}", userStandardVO);
        if (userStandardVO == null) {
            String message = "修改用户失败，用户不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, message);
        }
        String name = userUpdateDTO.getUsername();
        int rows = userMapper.countByName(name);
        log.debug("根据用户名【{}【统计数量，结果为：{}", name, rows);
        if (rows > 0) {
            String message = "修改用户失败，用户名已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_DATA_PRESENCE, message);
        }

        String email = userUpdateDTO.getEmail();
        rows = userMapper.countByEmail(email);
        log.debug("根据用户名【{}【统计数量，结果为：{}", email, rows);
        if (rows > 0) {
            String message = "修改用户失败，邮箱已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_DATA_PRESENCE, message);
        }

        String identityCard = userUpdateDTO.getIdentityCard();
        rows = userMapper.countByIdentityCard(identityCard);
        log.debug("根据用户名【{}【统计数量，结果为：{}", identityCard, rows);
        if (rows > 0) {
            String message = "修改用户失败，身份证号已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_DATA_PRESENCE, message);
        }

        String phone = userUpdateDTO.getPhone();
        rows = userMapper.countByPhone(phone);
        log.debug("根据用户名【{}【统计数量，结果为：{}", phone, rows);
        if (rows > 0) {
            String message = "修改用户失败，电话号码已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_DATA_PRESENCE, message);
        }

        User user = new User();
        BeanUtils.copyProperties(userUpdateDTO, user);
        user.setId(id);
        log.debug("即将修改用户数据，参数为：{}", user);
        rows = userMapper.update(user);
        if (rows != 1) {
            String message = "修改用户失败，请稍后再试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_UNKNOWN, message);
        }
        log.debug("修改数据成功，修改后参数为：{}", user);

    }

    @Override
    public void setEnable(Long id) {
        log.debug("开始处理【启用状态】的业务，参数：{}", id);
        updateStateById(id, 1);
    }

    @Override
    public void setDisable(Long id) {
        log.debug("开始处理【禁用状态】的业务，参数：{}", id);
        updateStateById(id, 0);
    }

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public UserStandardVO selectById(Long id) {
        UserStandardVO userStandardVO = userMapper.getStandardById(id);
        if (userStandardVO == null || userStandardVO.getState().equals(StateCode.STATE_DELETE.getValue())) {
            String message = "查询用户失败，数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, message);
        }
        return userStandardVO;
    }


    @Override
    public List<UserListItemVO> selectByLike(UserLikeDTO userLikeDTO) {
        log.debug("开始查询用户列表");
        List<UserListItemVO> list = userMapper.selectByLike(userLikeDTO);
        list.removeIf(user -> user.getState().equals(StateCode.STATE_DELETE.getValue()));

        return list;
    }

    /**
     * 查询数据列表
     *
     * @return 实例对象列表
     */
    @Override
    public List<UserListItemVO> selectList() {
        List<UserListItemVO> list = userMapper.selectList();
        list.removeIf(user -> user.getState().equals(StateCode.STATE_DELETE.getValue()));
        return list;
    }

    private void updateStateById(Long id, Integer state) {
        // 调用Mapper对象的getStandardById()方法执行查询
        UserStandardVO standardById = userMapper.getStandardById(id);
        // 判断查询结果是否为null，如果是，则抛出异常
        if (standardById == null) {
            String message = ENABLE_TEXT[state] + "类别失败，类别数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_NOT_FOUND, message);
        }

        // 判断查询结果中的enable与参数enable是否相同，如果是，则抛出异常（当前状态与目标状态相同，没必要执行更新）
        if (standardById.getState().equals(state)) {
            String message = ENABLE_TEXT[state] + "类别失败，此类别已经处于" + ENABLE_TEXT[state] + "状态！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_CONFLICT, message);
        }

        // 创建Category对象
        User user = new User();
        // 向user对象中封装属性值：id, enable，均来自方法参数
        user.setId(id);
        user.setState(state);
        // 调用Mapper对象的update()方法执行更新
        int rows = userMapper.update(user);
        if (rows != 1) {
            String message = ENABLE_TEXT[state] + "类别失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERROR_UPDATE, message);
        }
    }

}
